/*
 * Created on 2005/04/26
 * Author aki@www.xucker.jpn.org
 * License Apache2.0 or Common Public License
 */
package jp.sourceforge.akjrcp.snack;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;

import org.apache.commons.lang.ArrayUtils;

import jp.sourceforge.akjrcp.ui.ProgressMonitor;

/**
 * 
 *
 */
public class SpectrogramParser extends AbstractParser{
   
    private ProgressMonitor progressMonitor;
    private int perProgress=100;
    
    private int samplerate;
    private int height;
    private int permillisecond;
    
    private boolean removeNoise=true;
    
    public int getHeight() {
        return height;
    }
    public void setHeight(int height) {
        this.height = height;
    }
    public int getPermillisecond() {
        return permillisecond;
    }
    public void setPermillisecond(int permillisecond) {
        this.permillisecond = permillisecond;
    }
    public boolean isRemoveNoise() {
        return removeNoise;
    }
    public void setRemoveNoise(boolean removeNoise) {
        this.removeNoise = removeNoise;
    }
    public int getSamplerate() {
        return samplerate;
    }
    public void setSamplerate(int samplerate) {
        this.samplerate = samplerate;
    }
    
    public static int toValue256(double d){
        //
        if(d<0){
            d*=-1;
        }
        double r=Math.toRadians(d);
        double v=Math.sin(r)*1.1;//*1.1;
       // log.info"+r+","+v);
        /*
         * int c=0;
        c=Math.max(0,(int)((double)-(d-68)*2));
        c=Math.min(c,255);
        c=Math.max(0,c*c/256);
        return 255-c;*/
        
        return Math.min(255,(int)(v*256));
    }
    
   public SpectrogramParser(int samplerate,int height,int permillisecond){
       this.samplerate=samplerate;
       this.height=height;
       this.permillisecond=permillisecond;
   }
        public Object parse(Reader read) throws IOException{
            ArrayList<Integer> blist=new ArrayList<Integer>();
            Spectrogram spectrogram=new Spectrogram();
            spectrogram.setHeight(height);
            spectrogram.setSamplerate(samplerate);
            spectrogram.setPerSample(permillisecond);
            
            int index=0;
            int tmpPer=perProgress*height;
            String line;
            BufferedReader reader=new  BufferedReader(read);
            int prev=0;
            //this version return diff of prev value.becouse of decrease data size.
            while((line=reader.readLine())!=null){
                int v=Integer.parseInt(line);
                prev=prev-v;
                blist.add(prev);
                if(progressMonitor!=null && index%tmpPer==0){
                    if(progressMonitor.isCanceled()){
                        reader.close();
                        return null;
                    }
                    progressMonitor.worked(perProgress);//finished line.//ignore height
                	}
                index++;
            	}
            reader.close();
            int[] rawint=ArrayUtils.toPrimitive(blist.toArray(new Integer[blist.size()]));
            spectrogram.setRawInt(rawint);
            
            for(int i=0;i<rawint.length;i++){
                rawint[i]=toValue256(rawint[i]);
            }
            
            if(removeNoise){
                //this is cheep.
                new NoiseRemover(height).removeNoize(spectrogram.getRawInt());
            }
            
            return spectrogram;
        }
        /*
        public void  parseImage(Reader read) throws IOException{
            
            
            Spectrogram spectrogram=new Spectrogram();
            spectrogram.setHeight(height);
            spectrogram.setSamplerate(samplerate);
            spectrogram.setPerSample(permillisecond);
            
            int index=0;
            int tmpPer=perProgress*height;
            String line;
            BufferedReader reader=new  BufferedReader(read);
            int prev=0;
            //this version return diff of prev value.becouse of decrease data size.
            while((line=reader.readLine())!=null){
                int v=Integer.parseInt(line);
                prev=prev-v;//write diff values.
                blist.add(toSignedByte(toValue256(prev)));
                if(progressMonitor!=null && index%tmpPer==0){
                    if(progressMonitor.isCanceled()){
                        reader.close();
                        return ;
                    }
                    progressMonitor.worked(perProgress);//finished line.//ignore height
                	}
                index++;
            	}
            reader.close();
            int[] rawint=blist.toArray();
            spectrogram.setRawInt(rawint);
            
            for(int i=0;i<rawint.length;i++){
                rawint[i]=toValue256(rawint[i]);
            }
            
            if(removeNoise){
                //this is cheep.
                new NoiseRemover(height).removeNoize(spectrogram.getRawInt());
            }
            
            return spectrogram;
        }
        */
        public byte toSignedByte(int v){
            byte r=(byte)v;
            if(r>127){
                r-=256;
            }
            return r;
        }
        public static class NoiseRemover{
        private int sampleHeight;
        public NoiseRemover(int sampleheight){
            this.sampleHeight=sampleheight;
        }
        private void removeNoize(int[] values2) {
            int vsize=10;
            int x=values2.length/sampleHeight;
            
            for(int i=0;i<x;i++){
                int last[]=new int[vsize-1];
                for(int j=0;j<sampleHeight;j++){
                    int v=values2[i*sampleHeight+j];
                    if(v!=0){
                        if(isNear(v,last)){
                            //log.trace("remove:"+i+","+j);
                            
                            for(int k=0;k<last.length+1;k++){
                                values2[i*sampleHeight+j-k]=255;
                            }
                            
                            setInit(0,last);
                        }else{
                            setLast(v,last);
                        }
                    }else{
                        setLast(v,last);
                    }
                }
            }
            
        }
        
        /**
         * @param values2
         */
        private boolean near(int a,int b){
            int ch=2;
            return Math.abs(a-b)<=ch;
        }
       
        private boolean isNear(int v,int[] array){
            for(int i=array.length-1;i>0;i--){
                if(!near(array[i],array[i-1])){
                    return false;
                }
            }
            return near(array[0],v);
            
        }
        private void setInit(int value,int array[]){
            for(int i=0;i<array.length;i++){
                array[i]=value;
            }
        }
        private void setLast(int v,int array[]){
            for(int i=array.length-1;i>0;i--){
                array[i]=array[i-1];
            }
            array[0]=v;
        }
        }
    public ProgressMonitor getProgressMonitor() {
        return progressMonitor;
    }
    public void setProgressMonitor(ProgressMonitor progressMonitor) {
        this.progressMonitor = progressMonitor;
    }
}
